﻿using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Core.Objects;
using System.Linq;

namespace DynamicFiltering
{

    /// <summary>
    /// Wrapper for the list of navigation items needed by EntityModelNavigator.
    /// Also used in other cases when determining relationships between entities is needed.
    /// </summary>
    public class EntityNavigationList
    {

        private readonly List<NavigationItem> _navItems;

        public List<NavigationItem> List { get { return _navItems; } }

        public EntityNavigationList( ObjectContext context )
        {
            _navItems = InitializeNavigationItems( context );
        }

        /// <summary>
        /// Create a list of all navigation items in the model
        /// </summary>
        private static List<NavigationItem> InitializeNavigationItems( ObjectContext context )
        {
            var entityMetadata = context.MetadataWorkspace.GetItems( DataSpace.CSpace );
            var entitySetMetadata = context.MetadataWorkspace.GetEntityContainer( context.DefaultContainerName, DataSpace.CSpace ).BaseEntitySets;

            var query = from meta in entityMetadata
                          .Where( m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType )
                        from p in ( meta as EntityType ).NavigationProperties
                        let foreignKey = ( p.ToEndMember.DeclaringType as AssociationType ).ReferentialConstraints.FirstOrDefault()
                        let foreignKeyColumn = foreignKey == null ? string.Empty : foreignKey.ToProperties.First().ToString()
                        let primaryKey = ( meta as EntityType ).KeyMembers
                        let primaryKeyColumn = primaryKey == null ? string.Empty : primaryKey.First().Name
                        let toMultiplicity = p.ToEndMember.RelationshipMultiplicity
                        let entitySet = ( from m in entitySetMetadata where m.ElementType.Name == p.FromEndMember.Name select m ).FirstOrDefault()
                        let entityProperties = ( entitySet == null ) ? null : ( entitySet.ElementType as EntityType ).Properties
                        select new NavigationItem
                        {
                            FromEntity = p.FromEndMember.Name,
                            ToEntity = p.ToEndMember.Name,
                            FromMultiplicity = p.FromEndMember.RelationshipMultiplicity,
                            ToMultiplicity = toMultiplicity,
                            ForeignKeyColumn = foreignKeyColumn,
                            PrimaryKeyColumn = primaryKeyColumn,
                            /// We need the corresponding entity set so we can get it's name (formula belongs to entity set "formulae")
                            EntitySet = entitySet,
                            ForeignKeyNullable = ( ( entityProperties == null ) || ( toMultiplicity == RelationshipMultiplicity.Many ) ) ? false : entityProperties[foreignKeyColumn].Nullable
                        };

            return query.ToList();
        }

    }

}
